home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr28 / pktmux11.zip / WINPKT.ASM < prev    next >
Assembly Source File  |  1993-03-01  |  17KB  |  649 lines

  1. ; File WINPKT.ASM                        8 Dec 1991
  2. ; Provides a Packet Driver interface between Windows 3 Enhanced mode 
  3. ; applications and a real Packet Driver. This attempts to solve the problem
  4. ; of Windows moving applications around in memory willy nilly. Install WINPKT
  5. ; after the Packet Driver and before starting Windows. 
  6. ; Command line is:
  7. ;    WINPKT  WINPKT_interrupt number PD_interrupt_number
  8. ; with both in the range of 60h to 7fh.
  9. ; Build with the Clarkson Packet Driver Collection subprograms:
  10. ;     masm WINPKT;
  11. ;     link WINPKT;
  12. ;     exe2bin WINPKT.EXE WINPKT.COM
  13. ;     del WINPKT.EXE
  14. ;
  15.     include defs.asm
  16.  
  17. ;    Copyright, 1988-9, 1990, Russell Nelson
  18. ;    Copyright, 1991, Roger F. James
  19. ;    Code revised slightly, formatting cleaned up enormously, added
  20. ;    documentation, Joe R. Doupnik, jrd@cc.usu.edu, Utah State Univ,
  21. ;    8 Dec 1991.
  22.  
  23. ;   This program is free software; you can redistribute it and/or modify
  24. ;   it under the terms of the GNU General Public License as published by
  25. ;   the Free Software Foundation, version 1.
  26. ;
  27. ;   This program is distributed in the hope that it will be useful,
  28. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  29. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  30. ;   GNU General Public License for more details.
  31. ;
  32. ;   You should have received a copy of the GNU General Public License
  33. ;   along with this program; if not, write to the Free Software
  34. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  35.  
  36. code    segment word public
  37.     assume  cs:code, ds:code, es:nothing
  38.  
  39.     org 2ch
  40. phd_environ dw  0
  41.     org 80h
  42. phd_dioa    label   byte
  43.     org 100h
  44. start:    jmp start_1
  45.     even
  46.  
  47. ;Debugging stuff
  48. ;total_pkts    dw  0
  49. ;gvm_pkts    dw  0
  50. ;bvm_pkts    dw  0
  51.  
  52. npacket_int_no  db  4 dup (0)    ; interrupt to communicate
  53.  
  54. per_handle  struc
  55. in_use        db  0        ; non-zero if this handle is in use
  56. their_handle    dw  0        ; lower layer handle
  57. recv_handler    dd  0        ; receiver upcall
  58. vm_id        dw  0        ; VM_ID for this handler
  59. per_handle  ends
  60.  
  61. handles    per_handle MAX_HANDLE dup(<>)
  62. end_handles label   byte
  63.  
  64.  
  65. regs    struc               ; stack offsets of incoming regs
  66.     _ES dw  ?
  67.     _DS dw  ?
  68.     _BP dw  ?
  69.     _DI dw  ?
  70.     _SI dw  ?
  71.     _DX dw  ?
  72.     _CX dw  ?
  73.     _BX dw  ?
  74.     _AX dw  ?
  75.     _IP dw  ?
  76.     _CS dw  ?
  77.     _F  dw  ?           ; flags, Carry flag is bit 0
  78. regs    ends
  79.  
  80. CY    equ 0001h
  81. EI    equ 0200h
  82.  
  83.  
  84. bytes   struc            ; stack offsets of incoming regs
  85.         dw  ?            ; es, ds, bp, di, si are 16 bits
  86.         dw  ?
  87.         dw  ?
  88.         dw  ?
  89.         dw  ?
  90.     _DL db  ?
  91.     _DH db  ?
  92.     _CL db  ?
  93.     _CH db  ?
  94.     _BL db  ?
  95.     _BH db  ?
  96.     _AL db  ?
  97.     _AH db  ?
  98. bytes   ends
  99.  
  100. their_isr    dd  0        ; original owner of pkt driver int
  101. old_isr        dd  0        ; old pkt driver int
  102. their_2f_isr    dd  0        ; original int 2f ISR
  103.  
  104. ; The following are globals that assume that the code that access them
  105. ; single threads.
  106. MAX_BUFFER_LEN  equ 1520
  107. our_buffer  db  MAX_BUFFER_LEN dup (0)
  108. buffer_flag db  0
  109. buffer_len  dw  0
  110. their_handler   dd  0        ; receiver handler to call
  111. their_bx    dw  0
  112. ;
  113. ;
  114.  
  115. vmm_running db  0        ; 386 virtual machine manager is running
  116. our_handle  dw  0        ; current top level handle
  117.  
  118. our_isr:
  119.     jmp our_isr_0        ; the required signature.
  120.     db  'PKT DRVR',0
  121.  
  122. our_isr_0:            ; check if it one of the calls we
  123.     assume  ds:nothing    ; want to intercept (passes addresses)
  124.     cmp    ah,2        ; f_access_type?
  125.     je    our_isr_2    ; e = yes
  126.     cmp    ah,3        ; f_release_type?
  127.     je    our_isr_2
  128.     cmp    ah,5        ; f_terminate
  129.     je    our_isr_2
  130.     cmp    ah,8        ; f_stop
  131.     je    our_isr_2
  132. our_isr_1:            ; nothing needing intercepion
  133.     jmp    old_isr         ; chain to the original Packet Driver
  134.  
  135. our_isr_2:
  136.     push    ax        ; We are interested in this one
  137.     push    bx        ; so save some registers
  138.     push    cx
  139.     push    dx
  140.     push    si
  141.     push    di
  142.     push    bp
  143.     push    ds
  144.     push    es
  145.     cld
  146.     mov    bx,cs        ; set up DS
  147.     mov    ds,bx
  148.     assume    ds:code
  149.  
  150.     mov    bp,sp        ; use bp to access the original regs
  151.     and    _F[bp],not CY   ; start by clearing the carry flag
  152.  
  153.     cmp    ah,2            ; f_access_type?
  154.     jne    our_isr_3    ; ne = no (it's special here)
  155.     jmp    f_access_type    
  156. our_isr_3:
  157.     cmp    ah,3        ; f_release_type?
  158.     jne    our_isr_4    ; ne = no
  159.     jmp    f_release_type
  160. our_isr_4:
  161.     cmp    ah,5        ; f_terminate?
  162.     jne    our_isr_5    ; ne = no
  163.     jmp    f_terminate
  164. our_isr_5:
  165.     jmp    f_stop        ; must be f_stop
  166.  
  167. our_isr_error:
  168.     mov    _DH[bp],dh    ; error code
  169.     or    _F[bp],CY       ; return their carry flag
  170. our_isr_return:
  171.     pop    es
  172.     pop    ds
  173.     pop    bp
  174.     pop    di
  175.     pop    si
  176.     pop    dx
  177.     pop    cx
  178.     pop    bx
  179.     pop    ax
  180.     iret
  181.  
  182. ; Windows (and many many other programs) use the Int 2Fh Multiplexor link,
  183. ; and each is supposed to grab it's own calls as seen in AX, or chain them
  184. ; to the previous Int 2Fh owner. Windows 3 uses function 16h for most work.
  185. ; This is our Int 2Fh routine.
  186. our_2f_isr:
  187.     assume    ds:nothing, es:nothing
  188.     cmp    ax,1608h    ; Windows Enhanced "Init completed" broadcast?
  189.     jne    our_2f_1    ; ne = not that function, chain it
  190.     mov    vmm_running,1   ; remember that Windows has started
  191.     jmp    their_2f_isr    ; pass it on down the chain
  192. our_2f_1:
  193.     cmp    ax,1609h    ; Windows Enhanced "Begin Exit" broadcast?
  194.     jne    our_2f_2    ; ne = no
  195.     mov    vmm_running,0   ; remember that Windows has stopped
  196. our_2f_2:
  197.     jmp    their_2f_isr    ; propagate broadcasts down the Int 2Fh chain
  198.  
  199. our_handler:            ; Receive upcalls from the Packet Driver
  200.     or    ax,ax        ; first (AX=0) call to get buffer?
  201.     jnz    our_handler_2    ; nz = no, must be second upcall
  202.     cmp    buffer_flag,1    ; must buffer, is it free?
  203.     je    our_handler_1   ; e = no, busy
  204.     inc    cx        ; round up pkt size for 16 bit transfers
  205.     cmp    cx,MAX_BUFFER_LEN ; pkt larger than our buffer?
  206.     ja    our_handler_1   ; a = yes, too large
  207.     mov    buffer_flag,1   ; mark buffer as busy now
  208.     mov    buffer_len,cx   ; store pkt length
  209.     mov    ax,cs
  210.     mov    es,ax        ; segment of our buffer
  211.     mov    di,offset our_buffer ; tell PD es:di is the buffer address
  212.     retf
  213.  
  214. our_handler_1:
  215.     xor    ax,ax        ; reject the packet by returning es:di = NULL
  216.     mov    es,ax
  217.     xor    di,di
  218.     retf
  219.  
  220. our_handler_2:            ; second upcall, packet transfer complete
  221.     mov    their_bx,bx    ; save their registers
  222.     mov    dx,bx
  223.     mov    bx,cs        ; set our data segment
  224.     mov    ds,bx
  225.     assume    ds:code
  226.  
  227.     mov    bx,offset handles    ; array of PD handles
  228. our_handler_3:
  229.     cmp    [bx].their_handle,dx    ; dx is upcoming handle
  230.     je    our_handler_4        ; found their handle
  231.     add    bx,(size per_handle)    ; next handle
  232.     cmp    bx,offset end_handles   ; examined all handles?
  233.     jb    our_handler_3        ; b = no, continue.
  234.                             ; get here if no matching handle
  235.     mov    buffer_flag,0        ; mark the buffer free
  236.     retf
  237.  
  238. our_handler_4:                      ; found the handle
  239. ;    inc    total_pkts
  240.     mov    ax,[bx].recv_handler.segm ; appliation's call address
  241.     mov    their_handler.segm,ax
  242.     mov    ax,[bx].recv_handler.offs
  243.     mov    their_handler.offs,ax
  244.     push    bx
  245.     mov    ax,1683h        ; Windows, get current vir mach ident
  246.     int    2fh
  247.     mov    ax,bx        ; ident returned in bx
  248.     pop    bx
  249.     cmp    ax,[bx].vm_id    ; same as the one the app is using?
  250.     je    our_handler_5    ; e = yes, correct VM is running
  251.     jmp    our_handler_7   ; no, another VM is running, switch to wanted
  252. our_handler_5:
  253. ;    inc    gvm_pkts
  254.     call    pass_to_app    ; copy buffer to application, via double call
  255.     retf
  256.  
  257. pass_to_app:
  258.     xor    ax,ax        ; set up register for first upcall to app
  259.     mov    bx,their_bx    ; handle from Packet Driver
  260.     mov    cx,buffer_len    ; packet size
  261.     push    ds
  262.     call    their_handler    ; do first upcall (request buffer address)
  263.     pop    ds
  264.     mov    ax,es        ; check for 0:0 as reject value
  265.     or    ax,ax
  266.     jnz    pass_to_app_1    ; nz = have an address
  267.     or    di,di        ; check for 0:offset (rather unlikely)
  268.     jnz    pass_to_app_1    ; nz = have an offset
  269.     mov    buffer_flag,0   ; packet is being declined, free our buffer
  270.     ret
  271. pass_to_app_1:            ; copy from our buffer to app's es:di
  272.     push    di
  273.     mov    cx,buffer_len
  274.     mov    si,offset our_buffer
  275.     cld
  276.     rep    movsb        ; copy frame into apps buffer
  277.     mov    ax,1        ; set up regs for second upcall
  278.     mov    bx,their_bx    ; handle
  279.     mov    cx,buffer_len    ; report packet length too
  280.     pop    si
  281.     mov    dx,es
  282.     push    ds
  283.     mov    ds,dx
  284.     assume    ds:nothing
  285.  
  286.     call    their_handler    ; call the application
  287.     pop    ds
  288.     assume    ds:code
  289.     mov    buffer_flag,0    ; free buffer
  290.     ret
  291.  
  292. ; Windows Enhanced, request virtual machine in bx, and call back at es:di 
  293. ; when it's ready (which, knowing Windows, may take quite a while, hence 
  294. ; the requirement to buffer the packet to clear the lan board and ints).
  295. our_handler_7:
  296. ;    inc    bvm_pkts
  297.     mov    ax,1685h    ; request switch VMs and callback
  298.     mov    bx,[bx].vm_id    ; virtual machine of the app
  299.     xor    cx,cx        ; flags (bits 0 and 1), zero is don't wait
  300.     mov    dx,40h        ; dx:si is priority boost
  301.     xor    si,si
  302.     push    cs
  303.     pop    es        ; es:di is address of callback routine
  304.     mov    di,offset our_callback
  305.     int    2fh
  306.     retf
  307.  
  308.     assume ds:nothing
  309. our_callback:            ; get here with correct virtual machine
  310.     push    ax        ; call application twice (a la PD) to deliver
  311.     push    bx        ; the buffered packet
  312.     push    cx
  313.     push    dx
  314.     push    si
  315.     push    di
  316.     push    ds
  317.     push    es
  318.     push    bp
  319.     mov    ax,cs
  320.     mov    ds,ax        ; set up our ds
  321.     assume    ds:code
  322.  
  323.     call    pass_to_app
  324.     pop    bp
  325.     pop    es
  326.     pop    ds
  327.     assume  ds:nothing
  328.     pop    di
  329.     pop    si
  330.     pop    dx
  331.     pop    cx
  332.     pop    bx
  333.     pop    ax
  334.     iret
  335.  
  336.     assume  ds:code
  337. f_access_type:                ; register for a packet Type
  338.     mov    bx,offset handles    ; array of PD handles
  339. access_type_1:
  340.     cmp    [bx].in_use,0        ; is this handle in use?
  341.     je    access_type_2        ; e = yes, found a free one
  342.     add    bx,(size per_handle)    ; next handle
  343.     cmp    bx,offset end_handles   ; examined all handles?
  344.     jb    access_type_1        ; b = no, continue
  345.     jmp    access_type_space    ; no handle found, return error
  346. access_type_2:
  347.     mov    our_handle,bx        ; save our handle
  348.     mov    [bx].in_use,1        ; make handle as in-use
  349.     cmp    vmm_running,0        ; is Windows Enhanced mode running?
  350.     je    access_type_3        ; e = no, don't bother to redirect
  351.     mov    [bx].recv_handler.segm,es
  352.     mov    [bx].recv_handler.offs,di
  353.     mov    bx,cs
  354.     mov    es,bx
  355.     mov    di,offset our_handler    
  356. access_type_3:
  357.     push    ds
  358.     mov    bx,_DS[bp]
  359.     mov    ds,bx
  360.     assume  ds:nothing
  361.  
  362.     mov    bx,_BX[bp]        ; restore callers registers
  363.     pushf
  364.     call    old_isr            ; call Packet Driver
  365.     pop    ds
  366.     assume    ds:code
  367.     mov    bx,our_handle
  368.     jnc    access_type_4        ; nc = success
  369.     mov    [bx].in_use,0        ; failed, free our handle
  370.     jmp    our_isr_error
  371. access_type_4:
  372.     mov    [bx].their_handle,ax    ; handle returned in ax
  373.     mov    _AX[bp],ax        ; save return handle
  374.     cmp    vmm_running,0        ; Windows Enhanced mode running?
  375.     je    access_type_5        ; e = no
  376.     push    bx            ; Windows "Get Current Virtual Mach"
  377.     mov    ax,1683h
  378.     int    2fh            ; get current VM_ID to bx
  379.     mov    ax,bx
  380.     pop    bx
  381.     mov    [bx].vm_id,ax        ; save ident as part of our handle
  382. access_type_5:
  383.     jmp    our_isr_return
  384.  
  385. access_type_space:
  386.     mov    dh,NO_SPACE
  387.     jmp    our_isr_error
  388.  
  389. f_release_type:
  390.     mov    bx,_BX[bp]        ; restore callers registers
  391.     pushf
  392.     call    old_isr
  393.     jnc    release_type_1        ; nc = success
  394.     jmp    our_isr_error
  395. release_type_1:
  396.     mov    ax,_BX[bp]        ;just in case
  397.     mov    bx,offset handles
  398. release_type_2:
  399.     cmp    [bx].their_handle,ax    ; compare handles
  400.     je    release_type_3        ; e = found a match
  401.     add    bx,(size per_handle)    ; next handle
  402.     cmp    bx,offset end_handles   ; examined all handles?
  403.     jb    release_type_2        ; b = no, continue
  404.     jmp    err_bad_handle        ; no handle found, return error
  405. release_type_3:
  406.     mov    [bx].in_use,0        ; say handle is no longer in use
  407.     jmp    our_isr_return
  408.  
  409. err_bad_handle:
  410.     mov    dh,BAD_HANDLE        ; dh is error code
  411.     jmp    our_isr_error
  412.  
  413. f_terminate:
  414.     mov    bx,_BX[bp]          ; restore callers registers
  415.     pushf
  416.     call    old_isr
  417.     jnc    terminate_1         ; nc = success
  418.     jmp    our_isr_error
  419. terminate_1:
  420.     mov    ax,_BX[bp]          ; just in case
  421.     mov    bx,offset handles
  422. terminate_2:
  423.     cmp    [bx].their_handle,ax    ; compare handles
  424.     je    terminate_3         ; e = found a match
  425.     add    bx,(size per_handle)    ; next handle
  426.     cmp    bx,offset end_handles   ; examined all handles?
  427.     jb    terminate_2         ; b = no, continue
  428.     jmp    err_bad_handle          ; no match, return error
  429. terminate_3:
  430.     mov    [bx].in_use,0        ; mark handle as free
  431.     mov    bx,offset handles    ; check that all handles are free
  432. terminate_4:
  433.     cmp    [bx].in_use,0        ; is this handle free?
  434.     jne    terminate_5        ; ne = no, so can't exit completely
  435.     add    bx,(size per_handle)    ; next handle
  436.     cmp    bx,offset end_handles   ; examined all handles?
  437.     jb    terminate_4        ; b = no, continue examination
  438.     mov    al,npacket_int_no    ; release our_isr
  439.     mov    ah,25h
  440.     push    ds
  441.     lds    dx,their_isr        ; ds:dx is routine address
  442.     int    21h
  443.     pop    ds
  444.     mov    ah,35h            ; got owner of Int 2Fh
  445.     mov    al,2fh
  446.     int    21h            ; to see if it's still us
  447.     mov    ax,es
  448.     mov    cx,cs
  449.     cmp    ax,cx            ; our segment?
  450.     jne    terminate_5        ; ne = no, can't terminate
  451.     cmp    bx,offset our_2f_isr    ; our offset?
  452.     jne    terminate_5        ; ne = no, can't terminate
  453.     mov    al,2fh            ; restore Int 2f
  454.     mov    ah,25h
  455.     push    ds
  456.     lds    dx,their_2f_isr        ; previous owner now is current owner
  457.     int    21h
  458.     pop    ds
  459.  
  460.     push    cs
  461.     pop    es
  462.     mov    ah,49h            ; free our memory
  463.     int    21h
  464.     jmp    our_isr_return
  465. terminate_5:
  466.     mov    dh,CANT_TERMINATE    ; error code
  467.     jmp    our_isr_error
  468.  
  469. ; Stop the packet driver doing upcalls. Also a following terminate will
  470. ; always succed (no in use handles any longer).
  471. f_stop:
  472.     mov    bx,_BX[bp]        ; restore caller's registers
  473.     pushf
  474.     call    old_isr
  475.     mov    bx,offset handles
  476. f_stop_2:
  477.     mov    [bx].in_use,0        ; say handle is free
  478.     add    bx,(size per_handle)    ; next handle
  479.     cmp    bx,offset end_handles    ; done all?
  480.     jb    f_stop_2        ; b = not yet
  481.     clc
  482.     ret
  483.  
  484. end_resident    label byte
  485.     include    printnum.asm
  486.     include decout.asm
  487.     include digout.asm
  488.     include chrout.asm
  489.  
  490. usage_msg   label   byte
  491.     db    ' Usage: WINPKT <new_packet_int_number>'
  492.     db    ' <old_packet_int_number>',CR,LF
  493.     db    ' Install WINPKT after the regular Packet Driver, but'
  494.     db    cr,lf,' before starting Windows 3.$'
  495.  
  496. copyright_msg   label   byte
  497.     db    "Virtual packet driver for Windows 3",CR,LF
  498.     db    "Portions Copyright 1991 Roger F. James",CR,LF,'$'
  499.  
  500. copyleft_msg    label   byte
  501.     db "Packet driver skeleton copyright 1988-90, Russell Nelson.",CR,LF
  502.     db "This program is free software; see the file COPYING for details."
  503.     db    CR,LF
  504.     db    "NO WARRANTY; see the file COPYING for details.",CR,LF
  505.     db    CR,LF
  506. crlf_msg db    CR,LF,'$'
  507.  
  508. location_msg    db  'Packet driver is at segment $'
  509. packet_int_no   db  0
  510. opacket_int_no  db  4 dup (0)
  511.  
  512. already_msg    db  CR,LF,'There is already a packet driver at $'
  513. not_found_msg   db  CR,LF,'There is no packet driver at $'
  514. new_int_msg    db  CR,LF,'Virtual packet driver installed on new interrupt $'
  515. old_int_msg    db  'Using old interrupt $'
  516.  
  517. not_found_error:
  518.     mov    dx,offset not_found_msg
  519.     mov    di,offset opacket_int_no
  520.     call    print_number
  521.     mov    ax,4c05h        ; exit to DOS with errorlevel = 5
  522.     int    21h
  523.  
  524. already_error:
  525.     mov    dx,offset already_msg
  526.     mov    di,offset npacket_int_no
  527.     call    print_number
  528.     mov    ax,4c05h        ; give errorlevel 5
  529.     int    21h
  530.  
  531. usage_error:
  532.     mov    dx,offset usage_msg
  533. error:    mov    ah,9
  534.     int    21h
  535.     mov    ax,4c0ah            ; give errorlevel 10
  536.     int    21h
  537.  
  538. start_1:cld
  539.     mov    dx,offset copyright_msg
  540.     mov    ah,9
  541.     int    21h
  542.     mov    dx,offset copyleft_msg
  543.     mov    ah,9
  544.     int    21h
  545.     mov    si,offset phd_dioa+1
  546.     call    skip_blanks         ; end of line?
  547.     cmp    al,CR
  548.     je    usage_error        ; e = yes
  549.  
  550.     mov    dx,offset location_msg    ; display load address
  551.     mov    ah,9
  552.     int    21h
  553.     mov    ax,cs            ; cs as a word
  554.     call    wordout
  555.     mov    dx,offset crlf_msg
  556.     mov    ah,9
  557.     int    21h
  558.  
  559. chk_options:
  560.     call    skip_blanks
  561.     cmp    al,'-'          ; any options?
  562.     jne    no_more_opt
  563. usage_error_j_1:
  564.     jmp usage_error
  565. no_more_opt:
  566.     mov    di,offset npacket_int_no
  567.     call    get_number
  568.     call    skip_blanks
  569.     cmp    al,CR
  570.     je    usage_error
  571.     mov    di,offset opacket_int_no
  572.     call    get_number
  573.     call    skip_blanks         ; end of line?
  574.     cmp    al,CR
  575.     jne    usage_error        ; ne = no
  576.     mov    al,npacket_int_no
  577.     mov    packet_int_no,al
  578.     call    verify_packet_int
  579.     jnc    packet_int_ok        ; nc = success
  580.     jmp    error
  581. packet_int_ok:
  582.     jne    packet_int_unused
  583.     jmp    already_error        ; error if no Packet Driver
  584. packet_int_unused:
  585.  
  586.     mov    al,opacket_int_no
  587.     mov    packet_int_no,al
  588.     call    verify_packet_int
  589.     jnc    packet_int_ok_1
  590.     jmp    error
  591. packet_int_ok_1:
  592.     je    packet_int_used
  593.     jmp    not_found_error
  594. packet_int_used:
  595.     mov    ah,35h              ; get Packet Driver interrupt routine
  596.     mov    al,opacket_int_no
  597.     int    21h
  598.     mov    old_isr.offs,bx        ; save here
  599.     mov    old_isr.segm,es
  600.     mov    ah,35h            ; get Int 2Fh interrupt routine
  601.     mov    al,2fh
  602.     int    21h
  603.     mov    their_2f_isr.offs,bx    ; save here
  604.     mov    their_2f_isr.segm,es
  605.     mov    ah,25h          ;install our 2f interrupt
  606.     mov    dx,offset our_2f_isr
  607.     int    21h
  608.  
  609.     mov    dx,offset new_int_msg
  610.     mov    di,offset npacket_int_no
  611.     call    print_number
  612.     mov    dx,offset old_int_msg
  613.     mov    di,offset opacket_int_no
  614.     call    print_number
  615.     call    take_packet_int
  616.  
  617.     mov    ah,49h            ; free our environment, because
  618.     mov    es,phd_environ        ; we won't need it
  619.     int    21h
  620.     mov    bx,1                ; get the stdout handle
  621.     mov    ah,3eh            ; close it in case they redirected it
  622.     int    21h
  623.     mov    dx,offset end_resident
  624.     add    dx,0fh            ; round up to next highest paragraph
  625.     mov    cl,4
  626.     shr    dx,cl
  627.     mov    ah,31h            ; terminate, stay resident
  628.     xor    al,al
  629.     int    21h
  630.  
  631. take_packet_int:
  632.     mov    ah,35h            ; get new (our) "PD" interrupt
  633.     mov    al,npacket_int_no
  634.     int    21h
  635.     mov    their_isr.offs,bx    ; remember previous owner here
  636.     mov    their_isr.segm,es
  637.     mov    ah,25h            ; install our packet interrupt
  638.     mov    dx,offset our_isr
  639.     int    21h
  640.     ret
  641.  
  642.     include verifypi.asm
  643.     include getnum.asm
  644.     include getdig.asm
  645.     include skipblk.asm
  646.     include printea.asm
  647. code    ends
  648.     end    start
  649.